<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="3c4e0a25" id="3c4e0a25"><span data-lake-id="u95e0ea7d" id="u95e0ea7d">典型回答</span></h1>
  <p data-lake-id="u68642b29" id="u68642b29"><br></p>
  <p data-lake-id="uc1beb329" id="uc1beb329"><span data-lake-id="ua81a6dcb" id="ua81a6dcb">水平越权：是指攻击者尝试访问与当前用户具有相同权限的其他用户资源。</span></p>
  <p data-lake-id="u21f298b8" id="u21f298b8"><span data-lake-id="udb71efe7" id="udb71efe7"><br>
    如：用户管理系统中，可以通过uid查询用户信息（包括用户手机号、家庭住址等敏感信息），且uid为数据库自增主键。此时攻击者可用自己的uid向下递减或向上递增试探性查询其他用户信息，导致用户信息泄露。</span></p>
  <p data-lake-id="ud2ade863" id="ud2ade863"><br></p>
  <p data-lake-id="u588ca0ac" id="u588ca0ac"><span data-lake-id="ua4949cd2" id="ua4949cd2">防范方法：无限增大攻击者攻击成本，包括不限于：</span></p>
  <p data-lake-id="u73ef0149" id="u73ef0149"><br></p>
  <ol list="uc9048e7a">
   <li fid="uac08f643" data-lake-id="ufb0e1fc4" id="ufb0e1fc4"><span data-lake-id="u417992e9" id="u417992e9">参数以post方式提交，攻击者需要抓包才能看到参数</span></li>
   <li fid="uac08f643" data-lake-id="ua1e9669c" id="ua1e9669c"><span data-lake-id="u36cda53c" id="u36cda53c">关键参数进行加密混淆（主要手段）</span></li>
   <li fid="uac08f643" data-lake-id="u1b32b906" id="u1b32b906"><span data-lake-id="uf4f30f57" id="uf4f30f57">限制ip、当前uid在单位时间内的请求频次</span></li>
   <li fid="uac08f643" data-lake-id="u5818b306" id="u5818b306"><span data-lake-id="u28510004" id="u28510004">主要字段，如uid，避免用自增ID生成。</span></li>
  </ol>
  <p data-lake-id="ubb56bdf1" id="ubb56bdf1"><br></p>
  <h1 data-lake-id="b3a8e5e2" id="b3a8e5e2"><span data-lake-id="uc16e9dae" id="uc16e9dae">扩展知识</span></h1>
  <p data-lake-id="u86827683" id="u86827683"><br></p>
  <h2 data-lake-id="hfHm9" id="hfHm9"><span data-lake-id="u6f654638" id="u6f654638">加密混淆</span></h2>
  <p data-lake-id="u49f3f913" id="u49f3f913"><br></p>
  <p data-lake-id="u76bf3f42" id="u76bf3f42"><span data-lake-id="u8982ed6f" id="u8982ed6f">以用户信息管理uid横向越权为例，如何对uid进行加密混淆？</span></p>
  <p data-lake-id="u502eacff" id="u502eacff"><br></p>
  <p data-lake-id="ua8600c50" id="ua8600c50"><span data-lake-id="ue80ea698" id="ue80ea698">基本思路：由于是查询接口，可以对uid直接进行对称加密，客户端或浏览器只要拿到加密后的密文提交查询请求即可，不需要知道uid明文是什么。服务端可以通过存储在服务器上的私钥进行解密拿到uid明文去查询数据库。</span></p>
  <p data-lake-id="u620d8fb3" id="u620d8fb3"><br></p>
  <ul list="u5c93b469">
   <li fid="uc6cdb72b" data-lake-id="uf2cd3623" id="uf2cd3623"><span data-lake-id="u94d2a0fb" id="u94d2a0fb">方案1（不可行）：使用散列算法，如md5，会导致uid不可逆，无法直接拿到数据库中查询。由于md5是32位或16位，攻击者也很容易观察出密文采用何种算法。<br></span><code data-lake-id="u5583c400" id="u5583c400"><span data-lake-id="ub7f3698f" id="ub7f3698f">注意：md5用于生成摘要信息，并不是用来加密的。</span></code></li>
   <li fid="uc6cdb72b" data-lake-id="ue2b23442" id="ue2b23442"><span data-lake-id="uc3a30513" id="uc3a30513">方案2（可行）：使用对称加密，如AES，服务端存储私钥，对uid加密后下发。查询接口携带密文，服务端通过私钥解密出明文uid，查询数据库即可。</span></li>
   <li fid="uc6cdb72b" data-lake-id="u150d6cdc" id="u150d6cdc"><span data-lake-id="u950140e2" id="u950140e2">方案3（推荐）：使用对称加密后，密文固定且通常会很长，如：</span></li>
  </ul>
  <p data-lake-id="ua3529c6b" id="ua3529c6b"><br></p>
  <pre lang="java"><code>
明文uid为1，私钥为123456，AES加密后密文为：
U2FsdGVkX1/fFmiULJXOvhkdWdBZLnf2Nw3UyFdF60g=
</code></pre>
  <p data-lake-id="u359b5ee7" id="u359b5ee7"><br></p>
  <p data-lake-id="u20f08ce6" id="u20f08ce6"><span data-lake-id="u6f0b586b" id="u6f0b586b">对于请求参数，要保证简单原则。同时如果能让每次请求uid密文变化，显然会大幅度增加攻击者的识别成本。<br></span><span data-lake-id="u7cafe503" id="u7cafe503">这里推荐开源的Sqids算法，</span><a href="https://sqids.org/java" target="_blank" data-lake-id="ue61ba360" id="ue61ba360"><span data-lake-id="u052284e2" id="u052284e2">官网链接</span></a><span data-lake-id="uaafcbeef" id="uaafcbeef">。</span></p>
  <p data-lake-id="ue3088a6e" id="ue3088a6e"><span data-lake-id="uc1e3c415" id="uc1e3c415">​</span><br></p>
  <p data-lake-id="ud3bde743" id="ud3bde743"><span data-lake-id="ufba18870" id="ufba18870">该算法本质是将一系列整数散列成一个字符串，并通过指定字符表的方式对散列进行多次打乱，确保不会被碰撞，且保证加密出的密文非常简短（同样是hash算法，md5可能出现碰撞且密文较长）。</span></p>
  <p data-lake-id="u02a7b7e9" id="u02a7b7e9"><span data-lake-id="uaf55479d" id="uaf55479d"><br>
    我们可以把uid+下发uid密文时的请求时间戳组合起来进行散列，这样确保了每次同一个uid其密文都不一样。客户端提交上来的密文，可以解出uid明文和时间戳。<br>
    使用方法：</span></p>
  <p data-lake-id="ubb35fca3" id="ubb35fca3"><br></p>
  <pre lang="java"><code>
// maven引入坐标
&lt;dependency&gt;
    &lt;groupId&gt;org.sqids&lt;/groupId&gt;
    &lt;artifactId&gt;sqids_3&lt;/artifactId&gt;
    &lt;version&gt;0.5.0&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
  <p data-lake-id="u09fcd9ca" id="u09fcd9ca"><br></p>
  <pre lang="java"><code>
// 创建sqids对象
SqidsOptions options=new SqidsOptions();
options.Alphabet="TGEpuRNDVtYvISsh34jz5c1db8eoPin6CJUgQwMAmLK9Farl2fW0OyHxqXkBZ7"; //指定字符表，确保密文不会被碰撞
Sqids sqids=new Sqids(options);
// 加密
String id=sqids.encode(Arrays.asList(1L,1696867066L)); //假设uid为1，时间戳为1696867066，加密后的密文为 W8mmdRiM6
// 解密
List&lt;Long&gt; numbers=sqids.decode(id); // 解密结果为[1, 1696867066]
</code></pre>
 </body>
</html>